#ifndef BLOCKQUEUE_H
#define BLOCKQUEUE_H

#include <deque>
#include <mutex>
#include <condition_variable>
#include <sys/time.h>
#include <chrono>

template <typename T>
class BlockQueue {
public:
    explicit BlockQueue(size_t maxCapacity = 1000);
    ~BlockQueue();

    void clear();
    void close();
    bool empty();
    bool full();

    size_t size();
    size_t capacity();

    T front();
    T back();

    void push_back(const T& item);
    void push_front(const T& item);

    bool pop(T& item);
    bool pop(T& item, int timeOut);

    void flush();
private:
    std::deque<T> deq_;
    size_t capacity_;

    std::mutex mtx_;
    std::condition_variable producer_;
    std::condition_variable consumer_;
    bool isClose_;
};

template <typename T>
BlockQueue<T>::BlockQueue(size_t maxCapacity)
    : capacity_(maxCapacity)
{
    assert(maxCapacity > 0);
    isClose_ = false;
}

template <typename T>
BlockQueue<T>::~BlockQueue()
{
    close();
}

template <typename T>
void BlockQueue<T>::close()
{
    {
        std::lock_guard<std::mutex> lock(mtx_);
        deq_.clear();
        isClose_ = true;
    }
    producer_.notify_all();
    consumer_.notify_all();
}

// 唤醒一个正在等待的消费者线程
template <typename T>
void BlockQueue<T>::flush()
{
    consumer_.notify_one();
}

// 清空
template <typename T>
void BlockQueue<T>::clear()
{
    std::lock_guard<std::mutex> lock(mtx_);
    deq_.clear();
}

template <typename T>
T BlockQueue<T>::front()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return deq_.front();
}

template <typename T>
T BlockQueue<T>::back()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return deq_.back();
}

template <typename T>
size_t BlockQueue<T>::size()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return deq_.size();
}

template <typename T>
size_t BlockQueue<T>::capacity()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return capacity_;
}

template <typename T>
void BlockQueue<T>::push_back(const T& item)
{
    std::unique_lock<std::mutex> lock(mtx_);
    producer_.wait(lock, [&]()->bool {
            return deq_.size() < capacity_;});
    deq_.push_back(item);
    consumer_.notify_one();
}

template <typename T>
void BlockQueue<T>::push_front(const T& item)
{
    std::unique_lock<std::mutex> lock(mtx_);
    while(deq_.size() >= capacity_)
        producer_.wait(lock);
    deq_.push_front(item);
    consumer_.notify_one();
}

template <typename T>
bool BlockQueue<T>::empty()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return deq_.empty();
}

template <typename T>
bool BlockQueue<T>::full()
{
    std::lock_guard<std::mutex> lock(mtx_);
    return deq_.size() >= capacity_;
}

template <typename T>
bool BlockQueue<T>::pop(T& item)
{
    std::unique_lock<std::mutex> lock(mtx_);
    while(deq_.empty()) {
        consumer_.wait(lock);
        if(isClose_) 
            return false;
    }

    item = deq_.front();
    deq_.pop_front();
    producer_.notify_one();
    return true;
}

template <typename T>
bool BlockQueue<T>::pop(T& item, int timeOut)
{
    std::unique_lock<std::mutex> lock(mtx_);
    while(deq_.empty()) {
        // 超时返回
        if(consumer_.wait_for(lock, std::chrono::seconds(timeOut))
            == std::cv_status::timeout)
        {
            return false;
        }
        if(isClose_)
            return false;
    }

    item = deq_.front();
    deq_.pop_front();
    producer_.notify_one();
    return true;
}

#endif